package main

import (
	"fmt"
	"sync"
	"sync/atomic"
	"time"
)

func main() {
	Run(100, update1)
	// Run(10, updateWithLock)
	Run(1, updateWithLock)
	Run2(100, updateWithAtomic)

}

type Person struct {
	Name string
	Age  int
}

// 情景举例
func Run(num int, upf func(*Person, string, int)) {
	p := Person{
		Name: "hahaha",
		Age:  1,
	}
	fmt.Println("初始：", p)
	wg := sync.WaitGroup{}
	wg.Add(num)

	for i := 0; i < num; i++ {
		go func() {
			num := i
			s := fmt.Sprintf("[name %d]->", num)
			upf(&p, s, num)
			wg.Done()
		}()
	}
	wg.Wait()
	fmt.Println("结束完成", p)
	// output:
	// 初始： {hahaha 1}
	// 结束完成 {[name 100]-> 31}
}

func update1(p *Person, name string, age int) {
	p.Name = name
	time.Sleep(200 * time.Millisecond) // 增加时间
	p.Age = age
}

// 所以应该怎么保证原子性更新？
// 要么是用锁，要么是使用原子操作

var mu sync.RWMutex

func updateWithLock(p *Person, name string, age int) {
	mu.Lock()
	defer mu.Unlock()
	p.Name = name
	time.Sleep(200 * time.Millisecond) // 增加时间
	p.Age = age
	// 这样子每一次等待，变成了串行，时间消耗太大
}

// 使用原子操作
// 在更新的时候将临时变量指针传递进去（保证了不会有别的实体更改到此指针中的值）
// 如果有需要了，再从atomic中load出来就可以
var atcVal atomic.Value

func updateWithAtomic(p *Person, name string, age int) {
	per := &Person{}
	per.Name = name
	time.Sleep(200 * time.Millisecond) // 增加时间
	per.Age = age
	atcVal.Store(per)
	// 利用strore的原子性，可以原子修改到atomic.Value中的值
}

func Run2(num int, upf func(*Person, string, int)) {
	p := Person{
		Name: "hahaha",
		Age:  1,
	}
	fmt.Println("初始：", p)
	wg := sync.WaitGroup{}
	wg.Add(num)

	for i := 0; i < num; i++ {
		go func() {
			num := i
			s := fmt.Sprintf("[name %d]->", num)
			upf(&p, s, num)
			wg.Done()
		}()
	}
	wg.Wait()
	fmt.Println("结束完成", p)
	fmt.Println("结束完成,load", atcVal.Load().(*Person))
}
